import { EntityNode } from './EntityNode';

/**
 * Component类：所有组件类的基类，提供组件的基本功能和接口。
 * 组件是节点的功能扩展单元，每个组件负责实现特定的功能。
 * 例如，渲染组件负责绘制，物理组件负责碰撞检测，脚本组件负责逻辑控制。
 *
 * @example
 * ```typescript
 * class RenderComponent extends Component {
 *   render() {
 *     // 实现渲染逻辑
 *   }
 * }
 *
 * const node = new EntityNode(new UiNode());
 * node.addComponent(RenderComponent);
 * ```
 */
export class Component<T = any> {
  /**
   * 组件的权重，数值越小权重越高，默认为 0。
   * @private
   */
  private _weight: number = 0;

  /**
   * 关联的节点。
   * @private
   */
  // @ts-expect-error
  private _node: EntityNode<T>;

  /**
   * 是否已经执行开始初始化函数？
   * @private
   */
  private isStart: boolean = false;

  /**
   * 组件是否启用
   * @private
   */
  private _enable: boolean = true;

  /**
   * 设置组件是否启用
   * @param value - 是否启用
   */
  set enable(value: boolean) {
    if (this._enable === value) {
      return;
    }

    try {
      const oldValue = this._enable;
      this._enable = value;

      if (value && this._node.enable) {
        if (!this.isStart) {
          this.start?.();
          this.isStart = true;
        }
        this.onEnable?.();
      } else if (!value && oldValue && this.isStart) {
        this.onDisable?.();
      }
    } catch (error) {
      console.error(`组件${this.constructor.name}状态切换失败:`, error);
      this._enable = !value; // 恢复原始状态
      throw error;
    }
  }

  /**
   * 获取组件是否启用
   * @returns 组件是否启用
   */
  get enable(): boolean {
    return this._enable;
  }

  /**
   * 设置组件的权重，数值越小权重越高。
   * 如果新值与当前值不同，则更新权重并通知关联的节点重新计算权重。
   * @param v - 新的权重值。
   */
  set weight(v: number) {
    if (this._weight === v) {
      console.warn('Component.weight：值与当前值相同');
      return;
    }
    this._weight = v;
    //@ts-expect-error
    this._node.updateWeights?.();
  }

  /**
   * 获取组件的权重。
   * @returns 当前的权重值。
   */
  get weight() {
    return this._weight;
  }

  /**
   * 获取关联的节点。
   * @returns 关联的节点实例。
   */
  get node(): EntityNode<T> {
    return this._node;
  }

  /**
   * 初始化组件并与节点关联。
   * @param node - 要关联的节点。
   */
  private init(node: EntityNode<T>) {
    this._node = node;
  }

  /**
   * 从当前节点上移除当前组件实例
   */
  destroy() {
    this._node.removeComponent(this.constructor as any);
  }

  /**
   * 在组件被添加到节点时调用，用于执行初始化逻辑。
   * 这是组件生命周期中的第一个调用方法，适合进行组件的初始设置、资源预加载等操作。
   * 注意：此时其他组件可能还未初始化完成，如需访问其他组件应在 start 中进行。
   */
  protected onLoad?(): void;

  /**
   * 在组件首次激活时调用，用于执行组件的启动逻辑。
   * 此时所有组件已完成初始化，可以安全地访问其他组件。
   * 适合进行依赖其他组件的初始化操作，如事件监听、组件间通信设置等。
   */
  protected start?(): void;

  /**
   * 每帧调用，用于执行需要持续更新的游戏逻辑。
   * 执行频率默认为60FPS，主要用于：
   * - 处理输入事件
   * - 更新游戏状态
   * - 执行动画和物理计算
   * - 处理AI逻辑
   *
   * @param deltaTime 自上次更新以来的时间差（毫秒）
   */
  protected update?(
    /**
     * 自上次更新以来的时间差，单位为毫秒
     */
    deltaTime: number
  ): void;

  /**
   * 在所有组件的 {@link update} 方法执行完后调用。
   * 这个方法特别适用于需要在所有更新完成后进行处理的逻辑，例如：
   * - 相机跟随：确保目标已完成移动后再更新相机位置
   * - UI更新：在游戏状态全部更新后刷新界面
   * - 后处理效果：在所有渲染完成后添加特效
   * - 数据同步：在所有状态更新后进行网络同步
   *
   * @param deltaTime 自上次更新以来的时间差（毫秒）
   */
  protected lateUpdate?(
    /**
     * 自上次更新以来的时间差，单位为毫秒
     */
    deltaTime: number
  ): void;

  /**
   * 当组件从禁用状态变为启用状态时调用。
   * 适用于：
   * - 重新激活时的状态恢复
   * - 事件监听器的重新绑定
   * - 资源的重新加载
   * - 定时器的重新启动
   */
  protected onEnable?(): void;

  /**
   * 当组件从启用状态变为禁用状态时调用。
   * 适用于：
   * - 暂停时的状态保存
   * - 事件监听器的解绑
   * - 资源的释放
   * - 定时器的暂停或清除
   */
  protected onDisable?(): void;

  /**
   * 当组件被永久性销毁时调用。
   * 这是组件生命周期的最后一个调用方法，用于：
   * - 清理所有事件监听
   * - 释放占用的系统资源
   * - 保存需要持久化的数据
   * - 断开与其他组件的引用关系
   */
  protected onDestroy?(): void;
}
